package main

import (
	"encoding/json"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"io"
	"myblog/db"
	"myblog/model"
	"net/http"
	"strconv"
	"strings"
	"sync"
)

// blogPosts 存储了所有博客文章
var blogPosts []model.BlogPost
var mutex sync.Mutex

func main() {
	db.InitDB("blog.db")

	http.HandleFunc("/posts", authMiddleware(postsHandler))
	http.HandleFunc("/posts/", authMiddleware(postHandler))

	http.HandleFunc("/register", registerHandler)
	http.HandleFunc("/login", loginHandler)

	// 监听并在8080端口启动服务器
	fmt.Println("Server is listening on port 8080...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		fmt.Println("Error starting server:", err)
	}
}

func postsHandler(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case http.MethodGet:
		mutex.Lock()
		postsJSON, _ := json.Marshal(blogPosts)
		mutex.Unlock()
		w.Header().Set("Content-Type", "application/json")
		w.Write(postsJSON)
	case http.MethodPost:
		var newPost model.BlogPost
		bodyBytes, err := io.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Invalid request", http.StatusBadRequest)
			return
		}
		err = json.Unmarshal(bodyBytes, &newPost)
		if err != nil {
			http.Error(w, "Invalid JSON", http.StatusBadRequest)
			return
		}

		id, err := db.CreatePost(newPost)
		if err != nil {
			http.Error(w, "Error saving post", http.StatusInternalServerError)
			return
		}

		newPost.ID = int(id)
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(newPost)
		w.WriteHeader(http.StatusCreated)
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func postHandler(w http.ResponseWriter, r *http.Request) {
	idStr := strings.TrimPrefix(r.URL.Path, "/posts/")
	id, err := strconv.Atoi(idStr)
	if err != nil {
		http.Error(w, "Invalid post ID", http.StatusBadRequest)
		return
	}

	switch r.Method {
	case http.MethodGet:
		post, err := db.GetPostByID(id)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		postJSON, _ := json.Marshal(post)
		w.Header().Set("Content-Type", "application/json")
		w.Write(postJSON)
	case http.MethodDelete:
		err := db.DeletePostByID(id)
		if err != nil {
			http.NotFound(w, r)
			return
		}
		w.WriteHeader(http.StatusOK)
	case http.MethodPut:
		var updatedPost model.BlogPost
		bodyBytes, err := io.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Invalid request", http.StatusBadRequest)
			return
		}
		err = json.Unmarshal(bodyBytes, &updatedPost)
		if err != nil {
			http.Error(w, "Invalid JSON", http.StatusBadRequest)
			return
		}

		err = db.UpdatePostByID(id, updatedPost)
		if err != nil {
			http.Error(w, "Error updating post", http.StatusInternalServerError)
			return
		}

		w.WriteHeader(http.StatusOK)
	default:
		w.WriteHeader(http.StatusMethodNotAllowed)
	}
}

func registerHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	var credentials struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}

	err := json.NewDecoder(r.Body).Decode(&credentials)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	err = db.RegisterUser(credentials.Username, credentials.Password)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(http.StatusCreated)
}

func loginHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}

	var credentials struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}

	err := json.NewDecoder(r.Body).Decode(&credentials)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	tokenString, err := db.AuthenticateUser(credentials.Username, credentials.Password)
	if err != nil {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}

	w.Header().Set("Token", tokenString)
	w.WriteHeader(http.StatusOK)
}

// Middleware to protect private routes
func authMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		tokenString := r.Header.Get("Authorization")
		claims := &model.Claims{}

		token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
			return db.JwtKey, nil
		})

		if err != nil {
			if err == jwt.ErrSignatureInvalid {
				w.WriteHeader(http.StatusUnauthorized)
				return
			}
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		if !token.Valid {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

		next.ServeHTTP(w, r)
	})
}
